<?php

/**
 * Texy! - web text markup-language (for PHP 4)
 * --------------------------------------------
 *
 * Copyright (c) 2004, 2008 David Grudl aka -dgx- (http://www.dgx.cz)
 *
 * This source file is subject to the GNU GPL license that is bundled
 * with this package in the file license.txt.
 *
 * For more information please see http://texy.info/
 *
 * @copyright  Copyright (c) 2004, 2008 David Grudl
 * @license    GNU GENERAL PUBLIC LICENSE version 2 or 3
 * @link       http://texy.info/
 * @package    Texy
 */



/**
 * Phrases module.
 *
 * @author     David Grudl
 * @copyright  Copyright (c) 2004, 2008 David Grudl
 * @package    Texy
 * @version    $Revision: 201 $ $Date: 2008-02-01 02:00:29 +0000 (Fri, 01 Feb 2008) $
 */
class TexyPhraseModule extends TexyModule
{

    var $tags = array(
        'phrase/strong' => 'strong', // or 'b'
        'phrase/em' => 'em', // or 'i'
        'phrase/em-alt' => 'em',
        'phrase/ins' => 'ins',
        'phrase/del' => 'del',
        'phrase/sup' => 'sup',
        'phrase/sup-alt' => 'sup',
        'phrase/sub' => 'sub',
        'phrase/sub-alt' => 'sub',
        'phrase/span' => 'a',
        'phrase/span-alt' => 'a',
        'phrase/cite' => 'cite',
        'phrase/acronym' => 'acronym',
        'phrase/acronym-alt' => 'acronym',
        'phrase/code'  => 'code',
        'phrase/quote' => 'q',
        'phrase/quicklink' => 'a',
    );


    /** @var bool  are links allowed? */
    var $linksAllowed = TRUE;



    function __construct($texy)
    {
        $this->texy = $texy;

        $texy->addHandler('phrase', array($this, 'solve'));

/*
        // UNIVERSAL
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#((?>([*+/^_"~`-])+?))(?!\s)(.*(?!\\2).)'.TEXY_MODIFIER.'?(?<!\s)\\1(?!\\2)'.TEXY_LINK.'??()#Uus',
            'phrase/strong'
        );
*/

        // ***strong+emphasis***
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\*)\*\*\*(?![\s*])(.+)'.TEXY_MODIFIER.'?(?<![\s*])\*\*\*(?!\*)'.TEXY_LINK.'??()#Uus',
            'phrase/strong+em'
        );

        // **strong**
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\*)\*\*(?![\s*])(.+)'.TEXY_MODIFIER.'?(?<![\s*])\*\*(?!\*)'.TEXY_LINK.'??()#Uus',
            'phrase/strong'
        );

        // //emphasis//
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<![/:])\/\/(?![\s/])(.+)'.TEXY_MODIFIER.'?(?<![\s/])\/\/(?!\/)'.TEXY_LINK.'??()#Uus',
            'phrase/em'
        );

        // *emphasisAlt*
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\*)\*(?![\s*])(.+)'.TEXY_MODIFIER.'?(?<![\s*])\*(?!\*)'.TEXY_LINK.'??()#Uus',
            'phrase/em-alt'
        );

        // ++inserted++
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\+)\+\+(?![\s+])([^\r\n]+)'.TEXY_MODIFIER.'?(?<![\s+])\+\+(?!\+)()#Uu',
            'phrase/ins'
        );

        // --deleted--
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<![<-])\-\-(?![\s>-])([^\r\n]+)'.TEXY_MODIFIER.'?(?<![\s<-])\-\-(?![>-])()#Uu',
            'phrase/del'
        );

        // ^^superscript^^
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\^)\^\^(?![\s^])([^\r\n]+)'.TEXY_MODIFIER.'?(?<![\s^])\^\^(?!\^)()#Uu',
            'phrase/sup'
        );

        // m^2 alternative superscript
        $texy->registerLinePattern(
            array($this, 'patternSupSub'),
            '#(?<=[a-z0-9])\^(-?[0-9]{1,4})(?![a-z0-9])#Uui',
            'phrase/sup-alt'
        );

        // __subscript__
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\_)\_\_(?![\s_])([^\r\n]+)'.TEXY_MODIFIER.'?(?<![\s_])\_\_(?!\_)()#Uu',
            'phrase/sub'
        );

        // m_2 alternative subscript
        $texy->registerLinePattern(
            array($this, 'patternSupSub'),
            '#(?<=[a-z])\_([0-9]{1,3})(?![a-z0-9])#Uui',
            'phrase/sub-alt'
        );

        // "span"
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\")\"(?!\s)([^\"\r]+)'.TEXY_MODIFIER.'?(?<!\s)\"(?!\")'.TEXY_LINK.'??()#Uu',
            'phrase/span'
        );

        // ~alternative span~
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\~)\~(?!\s)([^\~\r]+)'.TEXY_MODIFIER.'?(?<!\s)\~(?!\~)'.TEXY_LINK.'??()#Uu',
            'phrase/span-alt'
        );

        // ~~cite~~
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\~)\~\~(?![\s~])([^\r\n]+)'.TEXY_MODIFIER.'?(?<![\s~])\~\~(?!\~)'.TEXY_LINK.'??()#Uu',
            'phrase/cite'
        );

        // >>quote<<
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\>)\>\>(?![\s>])([^\r\n]+)'.TEXY_MODIFIER.'?(?<![\s<])\<\<(?!\<)'.TEXY_LINK.'??()#Uu',
            'phrase/quote'
        );

        // acronym/abbr "et al."((and others))
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!\")\"(?!\s)([^\"\r\n]+)'.TEXY_MODIFIER.'?(?<!\s)\"(?!\")\(\((.+)\)\)()#Uu',
            'phrase/acronym'
        );

        // acronym/abbr NATO((North Atlantic Treaty Organisation))
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(?<!['.TEXY_CHAR.'])(['.TEXY_CHAR.']{2,})()\(\((.+)\)\)#Uu',
            'phrase/acronym-alt'
        );

        // ''notexy''
        $texy->registerLinePattern(
            array($this, 'patternNoTexy'),
            '#(?<!\')\'\'(?![\s\'])([^'.TEXY_MARK.'\r\n]*)(?<![\s\'])\'\'(?!\')()#Uu',
            'phrase/notexy'
        );

        // `code`
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#\`(\S[^'.TEXY_MARK.'\r\n]*)'.TEXY_MODIFIER.'?(?<!\s)\`'.TEXY_LINK.'??()#Uu',
            'phrase/code'
        );


        // ....:LINK
        $texy->registerLinePattern(
            array($this, 'patternPhrase'),
            '#(['.TEXY_CHAR.'0-9@\#$%&.,_-]+)()(?=:\[)'.TEXY_LINK.'()#Uu',
            'phrase/quicklink'
        );


        $texy->allowed['phrase/ins'] = FALSE;
        $texy->allowed['phrase/del'] = FALSE;
        $texy->allowed['phrase/sup'] = FALSE;
        $texy->allowed['phrase/sub'] = FALSE;
        $texy->allowed['phrase/cite'] = FALSE;
    }



    /**
     * Callback for: **.... .(title)[class]{style}**:LINK.
     *
     * @param  TexyLineParser
     * @param  array      regexp matches
     * @param  string     pattern name
     * @return TexyHtml|string|FALSE
     */
    function patternPhrase($parser, $matches, $phrase)
    {
        list(, $mContent, $mMod, $mLink) = $matches;
        //    [1] => **
        //    [2] => ...
        //    [3] => .(title)[class]{style}
        //    [4] => LINK

        $tx = $this->texy;
        $mod = new TexyModifier($mMod);
        $link = NULL;

        $parser->again = $phrase !== 'phrase/code' && $phrase !== 'phrase/quicklink';

        if ($phrase === 'phrase/span' || $phrase === 'phrase/span-alt') {
            if ($mLink == NULL) {
                if (!$mMod) return FALSE; // means "..."
            } else {
                $link = $tx->linkModule->factoryLink($mLink, $mMod, $mContent);
            }

        } elseif ($phrase === 'phrase/acronym' || $phrase === 'phrase/acronym-alt') {
            $mod->title = trim(Texy::unescapeHtml($mLink));

        } elseif ($phrase === 'phrase/quote') {
            $mod->cite = $tx->blockQuoteModule->citeLink($mLink);

        } elseif ($mLink != NULL) {
            $link = $tx->linkModule->factoryLink($mLink, NULL, $mContent);
        }

        return $tx->invokeAroundHandlers('phrase', $parser, array($phrase, $mContent, $mod, $link));
    }



    /**
     * Callback for: any^2  any_2.
     *
     * @param  TexyLineParser
     * @param  array      regexp matches
     * @param  string     pattern name
     * @return TexyHtml|string|FALSE
     */
    function patternSupSub($parser, $matches, $phrase)
    {
        list(, $mContent) = $matches;
        $mod = new TexyModifier();
        $link = NULL;
        $mContent = str_replace('-', "\xE2\x88\x92", $mContent); // &minus;
        return $this->texy->invokeAroundHandlers('phrase', $parser, array($phrase, $mContent, $mod, $link));
    }



    /**
     * @param  TexyLineParser
     * @param  array      regexp matches
     * @param  string     pattern name
     * @return string
     */
    function patternNoTexy($parser, $matches)
    {
        list(, $mContent) = $matches;
        return $this->texy->protect(Texy::escapeHtml($mContent), TEXY_CONTENT_TEXTUAL);
    }



    /**
     * Finish invocation.
     *
     * @param  TexyHandlerInvocation  handler invocation
     * @param  string
     * @param  string
     * @param  TexyModifier
     * @param  TexyLink
     * @return TexyHtml
     */
    function solve($invocation, $phrase, $content, $mod, $link)
    {
        $tx = $this->texy;

        $tag = isset($this->tags[$phrase]) ? $this->tags[$phrase] : NULL;

        if ($tag === 'a')
            $tag = $link && $this->linksAllowed ? NULL : 'span';

        if ($phrase === 'phrase/code')
            $content = $tx->protect(Texy::escapeHtml($content), TEXY_CONTENT_TEXTUAL);

        if ($phrase === 'phrase/strong+em') {
            $el = TexyHtml::el($this->tags['phrase/strong']);
            $el->create($this->tags['phrase/em'], $content);
            $mod->decorate($tx, $el);

        } elseif ($tag) {
            $el = TexyHtml::el($tag); $el->setText($content);
            $mod->decorate($tx, $el);

            if ($tag === 'q') $el->attrs['cite'] = $mod->cite;
        } else {
            $el = $content; // trick
        }

        if ($link && $this->linksAllowed) return $tx->linkModule->solve(NULL, $link, $el);

        return $el;
    }

}
